--[[
	
	Name: Advanced Questbook
	By: Crypton

]]
AQB_VER_NUM = 0.94;
AQB_ADDON_NAME = "Advanced Quest Book";
AQB_TITLE_PAGE = AQB_ADDON_NAME.." v."..AQB_VER_NUM.." Beta";
AQB_PATH = "Interface/AddOns/AdvQuestBook/";
AQB_LANG_PATH = "Interface/AddOns/AdvQuestBook/local/";
AQB_ICON_PATH = "Interface/AddOns/AdvQuestBook/images/";
AQB_ICON_1 = AQB_ICON_PATH.."fin.png";
AQB_ICON_2 = AQB_ICON_PATH.."inc.png";
AQB_ICON_3 = AQB_ICON_PATH.."index.png";
AQB_ICON_4 = AQB_ICON_PATH.."item.png";
AQB_DEBUG_GAMESTRINGS = false;
AQB_UPDATE = false;
AQB_TTW = 3;

--[[

Starting at line 29 and ending at line 34, the code within those lines is
released as Attribution-Noncommercial-Share Alike 3.0 Unported by novayuna
of pbinfo addon: http://creativecommons.org/licenses/by-nc-sa/3.0/

]]
-- Begin Barrowed Function from PBInfo QuestTracker
function AQB_IsEmpty(str, zero)
	if (type(str) == "nil" or str == "" or (zero == true and str == 0)) then
		return true;
	end
	return false;
end
-- End Barrowed Function from PBInfo QuestTracker

function AQB_FileLoader(AQB_FPATH, AQB_FNAME)
	local func, err = loadfile(AQB_FPATH..AQB_FNAME);
	if (err) then
		DEFAULT_CHAT_FRAME:AddMessage(AQB_ISTRINGS["AQB_ERRFILELOAD"]..": "..AQB_FNAME.. " : "..err);
		return false;
	end
	dofile(AQB_FPATH..AQB_FNAME);
	return true;
end

AQB_LOCAL = GetLanguage();

if (AQB_LOCAL == "ENUS" or AQB_LOCAL == "ENEU") then
	AQB_LOCAL = "ENUS";
	AQB_LANG_FILE = "en";
elseif (AQB_LOCAL == "DE") then
	AQB_LOCAL = "DE";
	AQB_LANG_FILE = "de";
elseif (AQB_LOCAL == "FR") then
	AQB_LOCAL = "FR";
	AQB_LANG_FILE = "fr";
elseif (AQB_LOCAL == "ES") then
	AQB_LOCAL = "ES";
	AQB_LANG_FILE = "es";
else
	AQB_LOCAL = "ENUS";
	AQB_LANG_FILE = "en";
	DEFAULT_CHAT_FRAME:AddMessage("|cFFffffff"..AQB_ADDON_NAME..": does not currently support your language version of the game. Please contact Crypton on curse.com or curseforge.com if you would like to help add support for your language.|r");
end

AQB_FileLoader(AQB_LANG_PATH, AQB_LANG_FILE..".lua");
AQB_SSTRINGS = AQBPatterns[AQB_LOCAL];
AdvQuestBook_DataVer = AQB_SSTRINGS["AQB_DB_VER"];
AQB_FileLoader(AQB_LANG_PATH..AQB_LANG_FILE.."/", "AQBSnoopLocs_"..AQB_LANG_FILE..".lua");
AQB_FileLoader(AQB_LANG_PATH..AQB_LANG_FILE.."/", "AQBByName_"..AQB_LANG_FILE..".lua");
AQB_FileLoader(AQB_LANG_PATH..AQB_LANG_FILE.."/", "AQBByID_"..AQB_LANG_FILE..".lua");
AQB_FileLoader(AQB_LANG_PATH..AQB_LANG_FILE.."/", "AQBMapID_"..AQB_LANG_FILE..".lua");
AQB_FileLoader(AQB_LANG_PATH..AQB_LANG_FILE.."/", "AQBDataIndex_"..AQB_LANG_FILE..".lua");

function AQB_DecMagic(AQB_DECVAL, AQB_DEC)
	local aqbdec = tonumber(AQB_DEC);
	local aqbdecval = tonumber(AQB_DECVAL);
	local movedec = 10 ^ aqbdec;
	local result = aqbdecval * movedec;
	local shiftdec = 10 ^ 1;
	return math.floor(result * shiftdec) / shiftdec;
end

function AQB_DEBUG_MESSAGE(AQB_DBG_MSG)
	DEFAULT_CHAT_FRAME:AddMessage("AQB Debug: "..AQB_DBG_MSG);
	return;
end

AQB_Default_Config = {
	["version"] = AQB_VER_NUM,
	["minibook"] = {
		["x"] = "",
		["y"] = "",
		["show"] = true,
		["status"] = "unlocked",
	},
	["sharebtn"] = {
		["x"] = "",
		["y"] = "",
	},
	["settings"] = {
		[1] = {
			["name"] = "Show Tooltips",
			["value"] = true,
		},
		[2] = {
			["name"] = "Sticky Tips",
			["value"] = false,
		},
		[3] = {
			["name"] = "Sticky Icons",
			["value"] = true,
		},
		[4] = {
			["name"] = "Quest Description on Tooltips",
			["value"] = true,
		},
		[5] = {
			["name"] = "Recommended Level on Tooltips",
			["value"] = true,
		},
		[6] = {
			["name"] = "Rewards on Tooltips",
			["value"] = true,
		},
		[7] = {
			["name"] = "Coordinates on Tooltips",
			["value"] = true,
		},
		[8] = {
			["name"] = "Advanced Quest Dumper",
			["value"] = false,
		},
		[9] = {
			["name"] = "Preserve Incomplete",
			["value"] = true,
		},
		[10] = {
			["name"] = "Quest Share",
			["value"] = true,
		},
		[11] = {
			["name"] = "Snoop Transport",
			["value"] = true,
		},
		[12] = {
			["name"] = "Elite Trainers",
			["value"] = true,
		},
		[13] = {
			["name"] = "Show Shared on World Map",
			["value"] = true,
		},
		[14] = {
			["name"] = "Show message on Quest Dump",
			["value"] = false,
		},
	},
};

function AdvQuestBookConfig_Defaults()
	local i;
	local defaultcount = table.getn(AQB_Default_Config["settings"]);
	if (AQB_IsEmpty(AdvQuestBook_Config, true)) then
		AdvQuestBook_Config = AQB_Default_Config;
		DEFAULT_CHAT_FRAME:AddMessage(AQB_ADDON_NAME.." "..AQB_ISTRINGS["AQB_DFAULTSETLOAD"].."...");
	end
	for i = 1, defaultcount do
		if (AQB_IsEmpty(AdvQuestBook_Config["settings"][i], true)) then
			AQB_UPDATE = true;
			AdvQuestBook_Config["settings"][i] = AQB_Default_Config["settings"][i];
			SaveVariables("AdvQuestBook_Config");
		end
	end
	if (AQB_IsEmpty(AdvQuestBook_Config["minibook"], true)) then
		AQB_UPDATE = true;
		AdvQuestBook_Config["minibook"] = AQB_Default_Config["minibook"];
		SaveVariables("AdvQuestBook_Config");
	end
	if (AQB_IsEmpty(AdvQuestBook_Config["sharebtn"], true)) then
		AQB_UPDATE = true;
		AdvQuestBook_Config["sharebtn"] = AQB_Default_Config["sharebtn"];
		SaveVariables("AdvQuestBook_Config");
	end
	if (AQB_IsEmpty(AdvQuestBook_Config["version"], true)) then
		AQB_UPDATE = true;
		AdvQuestBook_Config["version"] = AQB_Default_Config["version"];
		SaveVariables("AdvQuestBook_Config");
	end
	if (AdvQuestBook_Config["version"] ~= AQB_VER_NUM) then
		AQB_UPDATE = true;
		AdvQuestBook_Config["version"] = AQB_VER_NUM;
		SaveVariables("AdvQuestBook_Config");
	end
	if (AQB_UPDATE) then
		DEFAULT_CHAT_FRAME:AddMessage("|cFF22ff17"..AQB_ADDON_NAME.."|r |cFFffffff"..AQB_ISTRINGS["AQB_SETSUPDATE"].."...|r");
		AQB_UPDATE = false;
	end
	return;
end

--[[

Seems that the fraction of a second between an event and getting passed
var1 especially sometimes gets replaced with the wrong information. Many
times arg1 becomes the value of the last msg sent in zone chat at the time.
This seems to have fixed the problem by passing args into a new var to
be carried along. Mainly happens during debug when calling another function
to handle something within the OnEvent call but to be safe, I am going
to leave it this way to help protect data integrity.

Just something to debug events and args with. Leaving at least for now until
I am sure I do not need it anymore.

]]
function AdvQuestBook_DebugArgs(event, dbg1, dbg2, dbg3, dbg4)
	local dbgargmsg = "";
	if (not AQB_IsEmpty(dbg1, true)) then
		dbgargmsg = dbgargmsg.." Arg1: "..dbg1.." Event: "..event;
	end
	if (not AQB_IsEmpty(dbg2, true)) then
		dbgargmsg = dbgargmsg.." Arg2: "..dbg2.." Event: "..event;
	end
	if (not AQB_IsEmpty(dbg3, true)) then
		dbgargmsg = dbgargmsg.." Arg3: "..dbg3.." Event: "..event;
	end
	if (not AQB_IsEmpty(dbg4, true)) then
		dbgargmsg = dbgargmsg.." Arg4: "..dbg4.." Event: "..event;
	end
	if (not AQB_IsEmpty(dbgargmsg, true)) then
		AQB_DEBUG_MESSAGE(dbgargmsg);
	end
	return dbgargmsg;
end

--[[

Made some drastic changes to the index system. To be more sufficient and further
reduce memory load only quests that are searched, in the logbook, or shared are
inserted into the global arrays instead of loading the whole data file and keeping
it in memory.

I excessively use loops, I know this, I like loops though :)

Probably don't really need this function, however, if sorted correctly then
it is useful in that File Loading only takes place when needed and not
on every loop of indexing specifically. Since LUA by default does not sort
an array when it saves/loads it it has no real order unless specificied.
Here we are making it sort the key vals to a proper order. :)

]]
function AdvQuestBook_SortByNum(t)
	local i;
	local j;
	local k = table.getn(t);
	local idxm;
	for i = 1, k, 1 do
		idxm = i;
		for j = i + 1, k, 1 do
			if (t[j] < t[idxm]) then
				idxm = j;
			end;
		end;	
		t[i], t[idxm] = t[idxm], t[i];
	end
	return t;
end

function AdvQuestBook_ConvertCSV(s, t)
	local start = 1;
	local i = start;
	local nexti;
	repeat
		nexti = string.find(s, ',', start);
		table.insert(t, string.sub(s, start, nexti - 1));
		start = nexti + 1;
	until start > string.len(s);
	return t;
end

function AdvQuestBook_LoadIndexes(datatype)
	local i;
	local j = table.getn(AQB_TEMP_QUESTS);
	local k;
	local l = table.getn(AQBDataIndex);
	local m, n;
	local qid;
	local curfile = 1;
	AQB_FileLoader(AQB_LANG_PATH..AQB_LANG_FILE.."/", "AQBData_"..AQB_LANG_FILE.."1.lua");
	for i = 1, j do
		qid = tonumber(AQB_TEMP_QUESTS[i]);
		for k = 1, l do
			if (qid >= AQBDataIndex[k]["start"] and qid <= AQBDataIndex[k]["end"] and k ~= curfile) then
				AQB_FileLoader(AQB_LANG_PATH..AQB_LANG_FILE.."/", "AQBData_"..AQB_LANG_FILE..tostring(k)..".lua");
				curfile = k;
				break;
			elseif (qid >= AQBDataIndex[k]["start"] and qid <= AQBDataIndex[k]["end"] and k == curfile) then
				break;
			end
		end
		for m, n in pairs(AdvQuestBookData) do
			if (m == qid) then
				if (datatype == "quest") then
					table.insert(AdvQuestBook_GlobalQuestIndex, m, n);
				elseif (datatype == "shared") then
					table.insert(AdvQuestBook_GlobalSharedIndex, m, n);
				elseif (datatype == "search") then
					table.insert(AdvQuestBook_GlobalSearchIndex, m, n);
				end
				break;
			end
		end
	end
	AQB_TEMP_QUESTS = {};
	return;
end

function AdvQuestBook_SetQuestIndexes()
	local i;
	local j;
	local k;
	local flag = false;
	local AQB_CUR_QUESTNUM;
	AQB_TEMP_QUESTS = {};
	local AQB_TOTALQUESTS = GetNumQuestBookButton_QuestBook();
	for i = 1, AQB_TOTALQUESTS do
		local AQB_INDEX, AQB_CATID, AQB_NAME, AQB_TRACK, AQB_LEVEL, AQB_DAILY = GetQuestInfo(i);
		local AQB_NAME = string.gsub(AQB_NAME, "%-+", "");
		AQB_NAME = string.gsub(AQB_NAME, "%s%s+", " ");
		AQB_NAME = string.gsub(AQB_NAME, AQB_SSTRINGS["AQB_QUEST_COMPLETE1"], "");
		if (not AQB_IsEmpty(AQBQbyName[AQB_NAME], true)) then
			AQB_CUR_QUESTNUM = AQBQbyName[AQB_NAME];
			k = table.getn(AQB_TEMP_QUESTS);
			for j = 1, k do
				if (AQB_TEMP_QUESTS[j] == AQB_CUR_QUESTNUM) then
					flag = true;
					break;
				end
			end
			if (not flag) then
				table.insert(AQB_TEMP_QUESTS, AQB_CUR_QUESTNUM);
			end
			flag = false;
		end
	end
	AQB_TEMP_QUESTS = AdvQuestBook_SortByNum(AQB_TEMP_QUESTS);
	AdvQuestBook_LoadIndexes("quest");
	return;
end

function AdvQuestBook_SetSharedIndexes()
	local l;
	local m;
	local n;
	local flag = false;
	local AQB_CUR_QUESTNUM;
	if (AQB_LAST_SHARE ~= 0) then
		AQB_TEMP_QUESTS = {};
		local l = table.getn(AQB_SharedQuests[AQB_LAST_SHARE]["quests"]);
		for i = 1, l do
			AQB_CUR_QUESTNUM = AQB_SharedQuests[AQB_LAST_SHARE]["quests"][i];
			AQB_CUR_QUESTNUM = tonumber(AQB_CUR_QUESTNUM);
			n = table.getn(AQB_TEMP_QUESTS);
			for m = 1, n do
				if (AQB_TEMP_QUESTS[m] == AQB_CUR_QUESTNUM) then
					flag = true;
					break;
				end
			end
			if (not flag) then
				table.insert(AQB_TEMP_QUESTS, AQB_CUR_QUESTNUM);
			end
			flag = false;
		end
	end
	AQB_TEMP_QUESTS = AdvQuestBook_SortByNum(AQB_TEMP_QUESTS);
	AdvQuestBook_LoadIndexes("shared");
	return;
end

function AdvQuestBook_SetSearchIndexes()
	local i, j;
	local k;
	local l;
	local AQB_CUR_QUESTNUM;
	AQB_TEMP_QUESTS = {};
	for i, j in pairs(AQBMF_SearchResults) do
		AQB_CUR_QUESTNUM = j["id"];
		l = table.getn(AQB_TEMP_QUESTS);
		for k = 1, l do
			if (AQB_TEMP_QUESTS[k] == AQB_CUR_QUESTNUM) then
				flag = true;
				break;
			end
		end
		if (not flag) then
			table.insert(AQB_TEMP_QUESTS, AQB_CUR_QUESTNUM);
		end
		flag = false;
	end
	AQB_TEMP_QUESTS = AdvQuestBook_SortByNum(AQB_TEMP_QUESTS);
	AdvQuestBook_LoadIndexes("search");
	return;
end

function AdvQuestBook_DefaultWhisperStatus()
	local wi;
	local getwhisp;
	local whispframe;
	for wi = 1, NUM_CHAT_WINDOWS do
		whispframe = getglobal("ChatFrame"..wi);
		getwhisp = ChatFrame_CheckedMessageGroup(whispframe, "WHISPER");
		AQB_Def_ChannelSets[wi] = getwhisp;
	end
	return;
end

function AdvQuestBook_GetCurrentWhisperStatus()
	local wi;
	local whispframe;
	for wi = 1, NUM_CHAT_WINDOWS do
		whispframe = getglobal("ChatFrame"..wi);
		ChatFrame_RemoveMessageGroup(whispframe, "WHISPER");
	end
	return;
end

function AdvQuestBook_SetCurrentWhisperStatus()
	local wi, wv;
	local whispframe;
	for wi, wv in pairs(AQB_Def_ChannelSets) do
		if (wv) then
			whispframe = getglobal("ChatFrame"..wi);
			ChatFrame_AddMessageGroup(whispframe, "WHISPER");
		end
	end
	AQB_FLAG_WHISPER = false;
	return;
end

function AdvQuestBook_AutoMoveTo(this)
	local icon = getglobal(this);
	info = {};
	info.text = UI_AUTO_MOVE; 
	info.value = 0;
	info.func = WorldMap_AutoMove(icon.mid, icon.x, icon.y);
	info.owner = DropDownframe;
end

function AdvQuestBook_eAutoMoveToByNPCID(this)
	local icon = getglobal(this);
	info = {};
	info.text = UI_AUTO_MOVE; 
	info.value = 0;
	info.func = WorldMap_AutoMoveByNpcID(icon.enpcid);
	info.owner = DropDownframe;
end

function AdvQuestBookGSubUTF8(str, start, numChars)
	local currentIndex = start;
	while (numChars > 0 and currentIndex <= #str) do
		local char = string.byte(str, currentIndex);
		if (char > 240) then
			currentIndex = currentIndex + 4;
		elseif (char > 225) then
			currentIndex = currentIndex + 3;
		elseif (char > 192) then
			currentIndex = currentIndex + 2;
		else 
			currentIndex = currentIndex + 1;
		end
		numChars = numChars - 1;
	end
	return str:sub(start, currentIndex - 1);
end
